import asyncio
import time
from datetime import datetime
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
import struct

from fastapi import FastAPI, HTTPException, UploadFile, File, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
import os
import shutil
import json
from enum import Enum
from projects import ProjectData, load_projects, save_projects
from test_result_handler import test_result_handler
from utils import PLCDataType, DataBlockVariable
from plc_config import DATA_BLOCK_VARIABLES, PLC_CONFIG, MONITOR_CONFIG
from plc_client import PLCClient, read_and_print_all_plc_data, get_sim_plc_data
from getOperationsFromExcel import get_operations_from_excel
from websocket_manager import set_main_event_loop, start_message_processor, manager
from websocket_routes import test_scan_simulation
from scanner_monitor import start_scanner_monitoring, stop_scanner_monitoring

import snap7
from snap7.util import *
import uvicorn

SIM = False

# 创建FastAPI应用
app = FastAPI(
    title="西门子S7-1200 PLC监控系统",
    description="基于S7协议的PLC数据监控系统，从数据块读取变量数据",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc"
)

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 全局PLC客户端实例
plc_client: Optional[PLCClient] = None

@app.on_event("startup")
async def startup_event():
    """应用启动时初始化PLC连接"""
    global plc_client
    print("🚀 启动西门子S7-1200 PLC监控系统")
    print(f"📡 目标PLC: {PLC_CONFIG['host']}")
    
    # 设置主事件循环并启动消息处理器
    set_main_event_loop(asyncio.get_event_loop())
    start_message_processor()
    
    
    plc_client = PLCClient(PLC_CONFIG)
    
    # 模拟模式警告
    if SIM:
        print("⚠️  警告：当前运行在模拟模式下，PLC数据为模拟数据！")
        print("⚠️  Warning: Running in simulation mode, PLC data is simulated!")
        print("🚀 跳过PLC连接，直接启动服务")
        
        # 启动扫码枪监控（模拟模式）
        plc_client.connect()
        start_scanner_monitoring(plc_client, sim_mode=True)
    else:
        # 只有在非模拟模式下才尝试连接PLC
        if plc_client.connect():
            print("✅ PLC连接成功")
            read_and_print_all_plc_data(plc_client, DATA_BLOCK_VARIABLES)
            # 启动扫码枪监控（实际模式）
            start_scanner_monitoring(plc_client, sim_mode=False)
        else:
            print("❌ PLC连接失败，请检查网络和配置")
    

@app.on_event("shutdown")
async def shutdown_event():
    """应用关闭时清理资源"""
    global plc_client
    
    # 停止扫码枪监控
    stop_scanner_monitoring()
    
    if plc_client:
        plc_client.disconnect()

@app.get("/")
async def root():
    """系统基本信息"""
    return {
        "message": "西门子S7-1200 PLC监控系统API",
        "version": "2.0.0",
        "status": "running",
        "plc_connected": plc_client.connected if plc_client else False,
        "plc_host": PLC_CONFIG["host"],
        "variables_count": len(DATA_BLOCK_VARIABLES),
        "timestamp": datetime.now().isoformat()
    }

@app.get("/api/v1/plc/status")
async def get_plc_status():
    """获取PLC连接状态"""
    if not plc_client:
        return {
            "connected": False,
            "error": "PLC客户端未初始化",
            "protocol": "S7",
            "host": PLC_CONFIG["host"],
            "timeout": PLC_CONFIG["timeout"]
        }
    
    return {
        "connected": plc_client.connected,
        "protocol": "S7",
        "host": PLC_CONFIG["host"],
        "rack": PLC_CONFIG["rack"],
        "slot": PLC_CONFIG["slot"],
        "timeout": PLC_CONFIG["timeout"],
    }

@app.get("/api/v1/plc/data")
async def get_plc_data():
    
    if SIM == True:
        return get_sim_plc_data()
    
    """获取当前PLC数据"""
    if not plc_client:
        raise HTTPException(status_code=500, detail="PLC客户端未初始化")
    
    if not plc_client.connected:
        raise HTTPException(status_code=503, detail="PLC未连接")
    
    # 读取所有数据块变量
    variable_data = plc_client.read_all_variables(DATA_BLOCK_VARIABLES)
    
    # 格式化数据
    formatted_data = {}
    for var_key, value in variable_data.items():
        # 获取变量的详细信息
        variable = plc_client.get_variable_info(var_key, DATA_BLOCK_VARIABLES)
        if variable:
            formatted_data[var_key] = {
                "name": variable.name,
                "value": value,
                "data_type": variable.data_type.value,
                "unit": variable.unit,
                "description": variable.description,
                "address": variable.get_address_string(),
                "db_number": variable.db_number,
                "start_offset": variable.start_offset,
                "bit_offset": variable.bit_offset if variable.data_type == PLCDataType.BOOL else None
            }
    
    return {
        "timestamp": datetime.now().isoformat(),
        "protocol": "S7",
        "data": formatted_data,
        "status": "success"
    }

# @app.post("/api/v1/plc/reconnect")
# async def reconnect_plc(): 
#     """重新连接PLC"""
#     if not plc_client:
#         raise HTTPException(status_code=500, detail="PLC客户端未初始化")
    
#     # 断开现有连接
#     if plc_client.connected:
#         plc_client.disconnect()
    
#     # 重新连接
#     success = plc_client.connect()
    
#     if success:
#         return {
#             "status": "success",
#             "message": "PLC重连成功",
#             "timestamp": datetime.now().isoformat()
#         }
#     else:
#         raise HTTPException(status_code=503, detail="PLC重连失败")

@app.get("/api/v1/variables")
async def get_variable_mapping():
    """获取数据块变量映射信息"""
    variables_info = {}
    for var_key, variable in DATA_BLOCK_VARIABLES.items():
        variables_info[var_key] = {
            "name": variable.name,
            "data_type": variable.data_type.value,
            "unit": variable.unit,
            "description": variable.description,
            "address": variable.get_address_string(),
            "db_number": variable.db_number,
            "start_offset": variable.start_offset,
            "bit_offset": variable.bit_offset if variable.data_type == PLCDataType.BOOL else None
        }
    
    return {
        "variables": variables_info,
        "monitored_variables": MONITOR_CONFIG["variables"],
        "protocol": "S7"
    }

@app.post("/api/v1/upload-excel")
async def upload_excel(file: UploadFile = File(...)):
    """上传Excel文件到curr_operations目录"""
    try:
        print(f"📤 收到文件上传请求")
        print(f"📄 文件名: {file.filename}")
        print(f"📋 文件类型: {file.content_type}")
        print(f"📏 文件大小: {file.size if hasattr(file, 'size') else '未知'}")
        
        # 清理 curr_operations 文件夹
        curr_operations_dir = "curr_operations"
        if os.path.exists(curr_operations_dir):
            for file_name in os.listdir(curr_operations_dir):
                file_path = os.path.join(curr_operations_dir, file_name)
                if os.path.isfile(file_path):
                    os.remove(file_path)
        
        # 创建目录（如果不存在）
        os.makedirs(curr_operations_dir, exist_ok=True)
        
        file_name = file.filename
        if not file_name:
            raise HTTPException(status_code=400, detail="文件名不能为空")
            
        # 验证文件类型
        if not file_name.endswith(('.xls', '.xlsx')):
            raise HTTPException(status_code=400, detail="只接受Excel文件(.xls或.xlsx)")
        
        # 构建目标文件路径
        target_path = os.path.join(curr_operations_dir, file_name)
        
        # 保存上传的文件
        try:
            content = await file.read()
            
            with open(target_path, "wb") as buffer:
                buffer.write(content)
            
            # 验证文件是否保存成功
            if os.path.exists(target_path):
                file_size = os.path.getsize(target_path)
            else:
                raise Exception("文件保存后验证失败")
                
        except Exception as save_error:
            raise Exception(f"文件保存失败: {save_error}")
        
        return {
            "status": "success",
            "timestamp": datetime.now().isoformat(),
            "message": "Excel文件上传成功",
            "file_name": file_name,
        }
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"文件上传失败: {str(e)}")

@app.get("/api/v1/operations")
async def get_operations():
    """从curr_operations目录读取Excel文件并获取工序步骤数据"""
    try:
        curr_operations_dir = "curr_operations"
        
        # 检查目录是否存在
        if not os.path.exists(curr_operations_dir):
            raise HTTPException(status_code=404, detail="curr_operations目录不存在")
        
        # 搜索Excel文件
        excel_files = []
        for file_name in os.listdir(curr_operations_dir):
            if file_name.endswith(('.xls', '.xlsx')):
                excel_files.append(file_name)
        
        # 检查Excel文件唯一性
        if len(excel_files) == 0:
            raise HTTPException(status_code=404, detail="curr_operations目录中未找到Excel文件")
        elif len(excel_files) > 1:
            raise HTTPException(status_code=400, detail=f"curr_operations目录中存在多个Excel文件: {', '.join(excel_files)}")
        
        # 获取唯一的Excel文件路径
        excel_file = excel_files[0]
        excel_path = os.path.join(curr_operations_dir, excel_file)
        
        # 调用get_operations_from_excel函数获取工序步骤
        operations = get_operations_from_excel(excel_path)
        
        return {
            "status": "success",
            "timestamp": datetime.now().isoformat(),
            "file_name": excel_file,
            "operations_count": len(operations),
            "operations": operations
        }
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"读取工序步骤失败: {str(e)}")

@app.get("/api/v1/projects/list")
async def get_projects():
    """获取所有项目数据"""
    try:
        projects = load_projects()
        return {
            "success": True,
            "data": projects,
            "message": "项目数据获取成功"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取项目数据失败: {str(e)}")

@app.post("/api/v1/projects/save")
async def save_projects_data(projects: List[ProjectData]):
    """保存项目数据"""
    try:
        # 转换为字典列表
        projects_data = [project.dict() for project in projects]
        if save_projects(projects_data):
            return {
                "success": True,
                "message": "项目数据保存成功"
            }
        else:
            raise HTTPException(status_code=500, detail="保存项目数据失败")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"保存项目数据失败: {str(e)}")


# ==================== Test Results API Routes ====================
@app.post("/api/v1/test-results/upload")
async def upload_test_results(test_data: Dict[str, Any]):
    """上传测试结果"""
    try:
        result = test_result_handler.save_test_result(test_data)
        
        if result["status"] == "success":
            return {
                "status": "success",
                "message": result["message"],
                "filename": result["filename"]
            }
        else:
            raise HTTPException(status_code=400, detail=result["message"])
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"上传测试结果失败: {str(e)}")



# ==================== Scanner API Routes ====================
@app.post("/api/v1/scanner/test-simulation")
async def api_test_scan_simulation():
    """测试扫码模拟"""
    return await test_scan_simulation()


@app.post("/api/v1/scanner/set-ready")
async def api_set_scanner_ready():
    """设置扫码枪监控就绪状态"""
    try:
        from scanner_monitor import set_scanner_ready, get_scanner_ready_status
        
        # 设置为就绪状态
        set_scanner_ready(True)
        
        return {
            "success": True,
            "message": "扫码枪监控已设置为就绪状态",
            "ready_status": get_scanner_ready_status()
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"设置扫码枪就绪状态失败: {str(e)}")


@app.get("/api/v1/scanner/ready-status")
async def api_get_scanner_ready_status():
    """获取扫码枪监控就绪状态"""
    try:
        from scanner_monitor import get_scanner_ready_status
        
        return {
            "success": True,
            "ready_status": get_scanner_ready_status()
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取扫码枪就绪状态失败: {str(e)}")


# ==================== WebSocket Routes ====================

@app.websocket("/ws/scanner")
async def websocket_endpoint(websocket: WebSocket):
    """WebSocket扫码器连接端点"""
    await manager.connect(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            # 处理接收到的消息（如果需要）
            print(f"收到WebSocket消息: {data}")
    except WebSocketDisconnect:
        manager.disconnect(websocket)


if __name__ == "__main__":
    print("🚀 启动西门子S7-1200 PLC监控系统")
    print(f"📡 目标PLC: {PLC_CONFIG['host']}")
    print("🔗 通信协议: S7")
    print(f"📍 S7连接: Rack {PLC_CONFIG['rack']}, Slot {PLC_CONFIG['slot']}")
    
    print(f"📊 监控变量: {len(DATA_BLOCK_VARIABLES)}个")
    for var_key, variable in DATA_BLOCK_VARIABLES.items():
        print(f"   - {var_key}: {variable.name} ({variable.get_address_string()})")
    
    print("🌐 启动Web服务器...")
    print("📖 API文档: http://localhost:8005/docs")
    print("🔗 系统状态: http://localhost:8005/")
    print("📊 数据接口: http://localhost:8005/api/v1/plc/data")
    print("📋 变量配置: http://localhost:8005/api/v1/variables")
    print("📤 上传Excel: http://localhost:8005/api/v1/upload-excel")
    print("📝 工序步骤: http://localhost:8005/api/v1/operations")
    print("🔌 WebSocket连接: ws://localhost:8005/ws/scanner")
    print("🧪 扫码测试: http://localhost:8005/api/v1/scanner/test-simulation")
    print("⏹️  按 Ctrl+C 停止服务")
    
    try:
        uvicorn.run(
            "main:app",
            host="0.0.0.0",
            port=8005,
            reload=False,
            log_level="info"
        )
    except KeyboardInterrupt:
        print("\n👋 系统已停止")
    except Exception as e:
        print(f"❌ 启动失败: {e}")
        
  